// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
// This source file is part of the Cangjie project, licensed under Apache-2.0
// with Runtime Library Exception.
//
// See https://cangjie-lang.cn/pages/LICENSE for license information.

#ifndef CANGJIE_MIGRATOR_RULES_RULELIST
#define CANGJIE_MIGRATOR_RULES_RULELIST

#include <memory>
#include <vector>

#include "cangjie/AST/Node.h"

#include "Actions/Action.h"
#include "Rules/MatchRule.h"
#include "Rules/Rule.h"
#include "TomlFile.h"
#include "UpdateInstance.h"

namespace CangjieMigrator::Rules {

// Trait to detect whether a type T has a static method with the signature:
// static <any> T::ExtractInfo(const AST::Node&, const PositionRange&)
template <typename T>
struct HasStaticExtractInfo {
private:
    // Attempt to call T::ExtractInfo(...) with expected argument types.
    // If it's well-formed, this overload will be selected via SFINAE.
    template <typename U>
    static auto Test(int) -> decltype(
        U::ExtractInfo(
            std::declval<const Cangjie::AST::Node&>(),
            std::declval<const PositionRange&>()
        ),
        std::true_type{}
    )
    {
        return std::true_type{};
    }

    // Fallback overload when the above is ill-formed.
    template <typename>
    static std::false_type Test(...)
    {
        return std::false_type{};
    }

public:
    // The result: true if T has the required static method, false otherwise.
    static constexpr bool value = decltype(Test<T>(0))::value;
};

class RuleList {
public:
    explicit RuleList(const TomlFile &toml);

    [[nodiscard]] bool IsEmpty() const;
    [[nodiscard]] const std::vector<std::unique_ptr<Rule>> &Get() const;
    [[nodiscard]] const std::vector<const Rule *> &Get(MatchRule::Type type) const;

    void Check(UpdateInstance::File &file, const Cangjie::AST::Node &node, const PositionRange &range) const;

private:
    std::vector<std::unique_ptr<Rule>> rules;
    std::map<MatchRule::Type, std::vector<const Rule *>> ruleMap;

    /**
     * Templated implementation of rule matching for a specific rule type.
     *
     * Enabled only if:
     *   - TMatchRule is a subclass of MatchRule (compile-time enforced)
     *   - TMatchRule has a static method with signature:
     *         static std::optional<ExtractedInfo> ExtractInfo(const AST::Node &, const PositionRange &)
     *
     * This method will attempt to extract relevant match information from the AST node,
     * and invoke all matching rules of the given type on that info.
     *
     * @tparam type        Enum value of the MatchRule::Type this function is specialized for.
     * @tparam TMatchRule  The corresponding concrete rule class (must inherit MatchRule and provide ExtractInfo).
     * @param file         The file match context used to record rule results and planned updates.
     * @param node         The AST node to match.
     * @param range        The AST node range.
     * @return true if any rule matched and emitted output; false otherwise.
     */
    template <
        MatchRule::Type type,
        typename TMatchRule,
        typename = std::enable_if_t<
            std::is_base_of_v<MatchRule, TMatchRule> &&        // TMatchRule must inherit from MatchRule
            HasStaticExtractInfo<TMatchRule>::value            // TMatchRule must have static ExtractInfo(...) method
        >
    >
    bool Check(UpdateInstance::File &file, const Cangjie::AST::Node &node, const PositionRange &range) const;
};

} // namespace CangjieMigrator::Rules

#endif // CANGJIE_MIGRATOR_RULES_RULELIST